home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / var / lib / python-support / python2.6 / xdg / Mime.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2009-04-20  |  14.6 KB  |  490 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. """
  5. This module is based on a rox module (LGPL):
  6.  
  7. http://cvs.sourceforge.net/viewcvs.py/rox/ROX-Lib2/python/rox/mime.py?rev=1.21&view=log
  8.  
  9. This module provides access to the shared MIME database.
  10.  
  11. types is a dictionary of all known MIME types, indexed by the type name, e.g.
  12. types['application/x-python']
  13.  
  14. Applications can install information about MIME types by storing an
  15. XML file as <MIME>/packages/<application>.xml and running the
  16. update-mime-database command, which is provided by the freedesktop.org
  17. shared mime database package.
  18.  
  19. See http://www.freedesktop.org/standards/shared-mime-info-spec/ for
  20. information about the format of these files.
  21.  
  22. (based on version 0.13)
  23. """
  24. import os
  25. import stat
  26. import fnmatch
  27. import xdg.BaseDirectory as xdg
  28. import xdg.Locale as xdg
  29. from xml.dom import Node, minidom, XML_NAMESPACE
  30. FREE_NS = 'http://www.freedesktop.org/standards/shared-mime-info'
  31. types = { }
  32. exts = None
  33. globs = None
  34. literals = None
  35. magic = None
  36.  
  37. def _get_node_data(node):
  38.     '''Get text of XML node'''
  39.     return []([ n.nodeValue for n in node.childNodes ]).strip()
  40.  
  41.  
  42. def lookup(media, subtype = None):
  43.     '''Get the MIMEtype object for this type, creating a new one if needed.'''
  44.     if subtype is None and '/' in media:
  45.         (media, subtype) = media.split('/', 1)
  46.     
  47.     if (media, subtype) not in types:
  48.         types[(media, subtype)] = MIMEtype(media, subtype)
  49.     
  50.     return types[(media, subtype)]
  51.  
  52.  
  53. class MIMEtype:
  54.     '''Type holding data about a MIME type'''
  55.     
  56.     def __init__(self, media, subtype):
  57.         """Don't use this constructor directly; use mime.lookup() instead."""
  58.         if not media or '/' not in media:
  59.             raise AssertionError
  60.         if not subtype or '/' not in subtype:
  61.             raise AssertionError
  62.         if not (media, subtype) not in types:
  63.             raise AssertionError
  64.         self.media = media
  65.         self.subtype = subtype
  66.         self._comment = None
  67.  
  68.     
  69.     def _load(self):
  70.         '''Loads comment for current language. Use get_comment() instead.'''
  71.         resource = os.path.join('mime', self.media, self.subtype + '.xml')
  72.         for path in xdg.BaseDirectory.load_data_paths(resource):
  73.             doc = minidom.parse(path)
  74.             if doc is None:
  75.                 continue
  76.             
  77.             for comment in doc.documentElement.getElementsByTagNameNS(FREE_NS, 'comment'):
  78.                 if not comment.getAttributeNS(XML_NAMESPACE, 'lang'):
  79.                     pass
  80.                 lang = 'en'
  81.                 goodness = 1 + (lang in xdg.Locale.langs)
  82.                 if goodness > self._comment[0]:
  83.                     self._comment = (goodness, _get_node_data(comment))
  84.                 
  85.                 if goodness == 2:
  86.                     return None
  87.             
  88.         
  89.  
  90.     
  91.     def get_comment(self):
  92.         '''Returns comment for current language, loading it if needed.'''
  93.         if self._comment is None:
  94.             self._comment = (0, str(self))
  95.             self._load()
  96.         
  97.         return self._comment[1]
  98.  
  99.     
  100.     def __str__(self):
  101.         return self.media + '/' + self.subtype
  102.  
  103.     
  104.     def __repr__(self):
  105.         if not self._comment:
  106.             pass
  107.         return '[%s: %s]' % (self, '(comment not loaded)')
  108.  
  109.  
  110.  
  111. class MagicRule:
  112.     
  113.     def __init__(self, f):
  114.         self.next = None
  115.         self.prev = None
  116.         ind = ''
  117.         while True:
  118.             c = f.read(1)
  119.             if c == '>':
  120.                 break
  121.             
  122.             ind += c
  123.         if not ind:
  124.             self.nest = 0
  125.         else:
  126.             self.nest = int(ind)
  127.         start = ''
  128.         while True:
  129.             c = f.read(1)
  130.             if c == '=':
  131.                 break
  132.             
  133.             start += c
  134.         self.start = int(start)
  135.         hb = f.read(1)
  136.         lb = f.read(1)
  137.         self.lenvalue = ord(lb) + (ord(hb) << 8)
  138.         self.value = f.read(self.lenvalue)
  139.         c = f.read(1)
  140.         if c == '&':
  141.             self.mask = f.read(self.lenvalue)
  142.             c = f.read(1)
  143.         else:
  144.             self.mask = None
  145.         if c == '~':
  146.             w = ''
  147.             while c != '+' and c != '\n':
  148.                 c = f.read(1)
  149.                 if c == '+' or c == '\n':
  150.                     break
  151.                 
  152.                 w += c
  153.             self.word = int(w)
  154.         else:
  155.             self.word = 1
  156.         if c == '+':
  157.             r = ''
  158.             while c != '\n':
  159.                 c = f.read(1)
  160.                 if c == '\n':
  161.                     break
  162.                 
  163.                 r += c
  164.             self.range = int(r)
  165.         else:
  166.             self.range = 1
  167.         if c != '\n':
  168.             raise 'Malformed MIME magic line'
  169.         c != '\n'
  170.  
  171.     
  172.     def getLength(self):
  173.         return self.start + self.lenvalue + self.range
  174.  
  175.     
  176.     def appendRule(self, rule):
  177.         if self.nest < rule.nest:
  178.             self.next = rule
  179.             rule.prev = self
  180.         elif self.prev:
  181.             self.prev.appendRule(rule)
  182.         
  183.  
  184.     
  185.     def match(self, buffer):
  186.         if self.match0(buffer):
  187.             if self.next:
  188.                 return self.next.match(buffer)
  189.             return True
  190.  
  191.     
  192.     def match0(self, buffer):
  193.         l = len(buffer)
  194.         for o in range(self.range):
  195.             s = self.start + o
  196.             e = s + self.lenvalue
  197.             if l < e:
  198.                 return False
  199.             if self.mask:
  200.                 test = ''
  201.                 for i in range(self.lenvalue):
  202.                     c = ord(buffer[s + i]) & ord(self.mask[i])
  203.                     test += chr(c)
  204.                 
  205.             else:
  206.                 test = buffer[s:e]
  207.             if test == self.value:
  208.                 return True
  209.         
  210.  
  211.     
  212.     def __repr__(self):
  213.         return '<MagicRule %d>%d=[%d]%s&%s~%d+%d>' % (self.nest, self.start, self.lenvalue, `self.value`, `self.mask`, self.word, self.range)
  214.  
  215.  
  216.  
  217. class MagicType:
  218.     
  219.     def __init__(self, mtype):
  220.         self.mtype = mtype
  221.         self.top_rules = []
  222.         self.last_rule = None
  223.  
  224.     
  225.     def getLine(self, f):
  226.         nrule = MagicRule(f)
  227.         if nrule.nest and self.last_rule:
  228.             self.last_rule.appendRule(nrule)
  229.         else:
  230.             self.top_rules.append(nrule)
  231.         self.last_rule = nrule
  232.         return nrule
  233.  
  234.     
  235.     def match(self, buffer):
  236.         for rule in self.top_rules:
  237.             if rule.match(buffer):
  238.                 return self.mtype
  239.         
  240.  
  241.     
  242.     def __repr__(self):
  243.         return '<MagicType %s>' % self.mtype
  244.  
  245.  
  246.  
  247. class MagicDB:
  248.     
  249.     def __init__(self):
  250.         self.types = { }
  251.         self.maxlen = 0
  252.  
  253.     
  254.     def mergeFile(self, fname):
  255.         f = file(fname, 'r')
  256.         line = f.readline()
  257.         if line != 'MIME-Magic\x00\n':
  258.             raise 'Not a MIME magic file'
  259.         line != 'MIME-Magic\x00\n'
  260.         while True:
  261.             shead = f.readline()
  262.             if not shead:
  263.                 break
  264.             
  265.             if shead[0] != '[' or shead[-2:] != ']\n':
  266.                 raise 'Malformed section heading'
  267.             shead[-2:] != ']\n'
  268.             (pri, tname) = shead[1:-2].split(':')
  269.             pri = int(pri)
  270.             mtype = lookup(tname)
  271.             
  272.             try:
  273.                 ents = self.types[pri]
  274.             except:
  275.                 ents = []
  276.                 self.types[pri] = ents
  277.  
  278.             magictype = MagicType(mtype)
  279.             c = f.read(1)
  280.             f.seek(-1, 1)
  281.             while c and c != '[':
  282.                 rule = magictype.getLine(f)
  283.                 if rule and rule.getLength() > self.maxlen:
  284.                     self.maxlen = rule.getLength()
  285.                 
  286.                 c = f.read(1)
  287.                 f.seek(-1, 1)
  288.             ents.append(magictype)
  289.             if not c:
  290.                 break
  291.                 continue
  292.  
  293.     
  294.     def match(self, path, max_pri = 100, min_pri = 0):
  295.         
  296.         try:
  297.             buf = file(path, 'r').read(self.maxlen)
  298.             pris = self.types.keys()
  299.             pris.sort((lambda a, b: -cmp(a, b)))
  300.             for pri in pris:
  301.                 if pri > max_pri:
  302.                     continue
  303.                 
  304.                 if pri < min_pri:
  305.                     break
  306.                 
  307.                 for type in self.types[pri]:
  308.                     m = type.match(buf)
  309.                     if m:
  310.                         return m
  311.                 
  312.         except:
  313.             pass
  314.  
  315.  
  316.     
  317.     def __repr__(self):
  318.         return '<MagicDB %s>' % self.types
  319.  
  320.  
  321. text = lookup('text', 'plain')
  322. inode_block = lookup('inode', 'blockdevice')
  323. inode_char = lookup('inode', 'chardevice')
  324. inode_dir = lookup('inode', 'directory')
  325. inode_fifo = lookup('inode', 'fifo')
  326. inode_socket = lookup('inode', 'socket')
  327. inode_symlink = lookup('inode', 'symlink')
  328. inode_door = lookup('inode', 'door')
  329. app_exe = lookup('application', 'executable')
  330. _cache_uptodate = False
  331.  
  332. def _cache_database():
  333.     global _cache_uptodate, exts, globs, literals, magic
  334.     _cache_uptodate = True
  335.     exts = { }
  336.     globs = []
  337.     literals = { }
  338.     magic = MagicDB()
  339.     
  340.     def _import_glob_file(path):
  341.         '''Loads name matching information from a MIME directory.'''
  342.         for line in file(path):
  343.             if line.startswith('#'):
  344.                 continue
  345.             
  346.             line = line[:-1]
  347.             (type_name, pattern) = line.split(':', 1)
  348.             mtype = lookup(type_name)
  349.             if pattern.startswith('*.'):
  350.                 rest = pattern[2:]
  351.                 if not '*' in rest and '[' in rest or '?' in rest:
  352.                     exts[rest] = mtype
  353.                     continue
  354.                 
  355.             
  356.             if '*' in pattern and '[' in pattern or '?' in pattern:
  357.                 globs.append((pattern, mtype))
  358.                 continue
  359.             literals[pattern] = mtype
  360.         
  361.  
  362.     for path in xdg.BaseDirectory.load_data_paths(os.path.join('mime', 'globs')):
  363.         _import_glob_file(path)
  364.     
  365.     for path in xdg.BaseDirectory.load_data_paths(os.path.join('mime', 'magic')):
  366.         magic.mergeFile(path)
  367.     
  368.     globs.sort((lambda a, b: cmp(len(b[0]), len(a[0]))))
  369.  
  370.  
  371. def get_type_by_name(path):
  372.     '''Returns type of file by its name, or None if not known'''
  373.     if not _cache_uptodate:
  374.         _cache_database()
  375.     
  376.     leaf = os.path.basename(path)
  377.     if leaf in literals:
  378.         return literals[leaf]
  379.     lleaf = leaf.lower()
  380.     if lleaf in literals:
  381.         return literals[lleaf]
  382.     ext = leaf
  383.     while None:
  384.         p = ext.find('.')
  385.         ext = ext[p + 1:]
  386.         if ext in exts:
  387.             return exts[ext]
  388.         continue
  389.         ext = lleaf
  390.         while None:
  391.             p = ext.find('.')
  392.             ext = ext[p + 1:]
  393.             if ext in exts:
  394.                 return exts[ext]
  395.             continue
  396.             for glob, mime_type in globs:
  397.                 if fnmatch.fnmatch(leaf, glob):
  398.                     return mime_type
  399.                 if fnmatch.fnmatch(lleaf, glob):
  400.                     return mime_type
  401.             
  402.  
  403.  
  404. def get_type_by_contents(path, max_pri = 100, min_pri = 0):
  405.     '''Returns type of file by its contents, or None if not known'''
  406.     if not _cache_uptodate:
  407.         _cache_database()
  408.     
  409.     return magic.match(path, max_pri, min_pri)
  410.  
  411.  
  412. def get_type(path, follow = 1, name_pri = 100):
  413.     '''Returns type of file indicated by path.
  414. \tpath\t - pathname to check (need not exist)
  415. \tfollow   - when reading file, follow symbolic links
  416. \tname_pri - Priority to do name matches.  100=override magic'''
  417.     if not _cache_uptodate:
  418.         _cache_database()
  419.     
  420.     
  421.     try:
  422.         if follow:
  423.             st = os.stat(path)
  424.         else:
  425.             st = os.lstat(path)
  426.     except:
  427.         t = get_type_by_name(path)
  428.         if not t:
  429.             pass
  430.         return text
  431.  
  432.     if stat.S_ISREG(st.st_mode):
  433.         t = get_type_by_contents(path, min_pri = name_pri)
  434.         if not t:
  435.             t = get_type_by_name(path)
  436.         
  437.         if not t:
  438.             t = get_type_by_contents(path, max_pri = name_pri)
  439.         
  440.         if t is None:
  441.             if stat.S_IMODE(st.st_mode) & 73:
  442.                 return app_exe
  443.             return text
  444.         t is None
  445.         return t
  446.     if stat.S_ISDIR(st.st_mode):
  447.         return inode_dir
  448.     if stat.S_ISCHR(st.st_mode):
  449.         return inode_char
  450.     if stat.S_ISBLK(st.st_mode):
  451.         return inode_block
  452.     if stat.S_ISFIFO(st.st_mode):
  453.         return inode_fifo
  454.     if stat.S_ISLNK(st.st_mode):
  455.         return inode_symlink
  456.     if stat.S_ISSOCK(st.st_mode):
  457.         return inode_socket
  458.     return inode_door
  459.  
  460.  
  461. def install_mime_info(application, package_file):
  462.     """Copy 'package_file' as ~/.local/share/mime/packages/<application>.xml.
  463. \tIf package_file is None, install <app_dir>/<application>.xml.
  464. \tIf already installed, does nothing. May overwrite an existing
  465. \tfile with the same name (if the contents are different)"""
  466.     global _cache_uptodate
  467.     application += '.xml'
  468.     new_data = file(package_file).read()
  469.     package_dir = os.path.join('mime', 'packages')
  470.     resource = os.path.join(package_dir, application)
  471.     for x in xdg.BaseDirectory.load_data_paths(resource):
  472.         
  473.         try:
  474.             old_data = file(x).read()
  475.         except:
  476.             continue
  477.  
  478.         if old_data == new_data:
  479.             return None
  480.     
  481.     _cache_uptodate = False
  482.     new_file = os.path.join(xdg.BaseDirectory.save_data_path(package_dir), application)
  483.     file(new_file, 'w').write(new_data)
  484.     command = 'update-mime-database'
  485.     if os.spawnlp(os.P_WAIT, command, command, xdg.BaseDirectory.save_data_path('mime')):
  486.         os.unlink(new_file)
  487.         raise Exception("The '%s' command returned an error code!\nMake sure you have the freedesktop.org shared MIME package:\nhttp://standards.freedesktop.org/shared-mime-info/") % command
  488.     os.spawnlp(os.P_WAIT, command, command, xdg.BaseDirectory.save_data_path('mime'))
  489.  
  490.